﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Globalization;

namespace BitcoinLibrary
{
    /// <summary>
    /// Unsigned biginteger class.
    /// </summary>
    public class BigInteger : IComparable, IComparable<BigInteger>
    {
        private System.Numerics.BigInteger bi;
        
        public BigInteger(int i)
        {
            Debug.Assert(i >= 0);
            bi = new System.Numerics.BigInteger(i);
        }

        public BigInteger(Int64 i)
        {
            Debug.Assert(i >= 0);
            bi = new System.Numerics.BigInteger(i);
        }

        private BigInteger(System.Numerics.BigInteger bi)
        {
            Debug.Assert(bi >= 0);
            this.bi = bi;
        }

        public BigInteger(byte[] bytes)
        {
            // http://stackoverflow.com/questions/5649190/byte-to-unsigned-biginteger
            var bytesWith00 = new byte[bytes.Length + 1];
            bytes.CopyTo(bytesWith00, 0);
            bytesWith00[bytes.Length] = 0;
            bi = new System.Numerics.BigInteger(bytesWith00);
        }

        public static BigInteger Zero 
        {
            get
            {
                return new BigInteger(System.Numerics.BigInteger.Zero);
            }
        }

        public static BigInteger One
        {
            get
            {
                return new BigInteger(System.Numerics.BigInteger.One);
            }
        }

        public static explicit operator int(BigInteger value)
        {
            return (int)value.bi;
        }

        public static explicit operator long(BigInteger value)
        {
            return (long)value.bi;
        }

        public static implicit operator BigInteger(int value)
        {
            return new BigInteger(value);
        }

        public static BigInteger operator <<(BigInteger value, int shift)
        {
            return new BigInteger(value.bi << shift);
        }

        public static bool operator <(BigInteger left, long right)
        {
            return left.bi < right;
        }

        public static bool operator >(BigInteger left, long right)
        {
            return left.bi > right;
        }

        public static BigInteger operator +(BigInteger left, BigInteger right)
        {
            return new BigInteger(left.bi + right.bi);
        }

        public static BigInteger operator -(BigInteger left, BigInteger right)
        {
            return new BigInteger(left.bi - right.bi);
        }

        public static BigInteger operator *(BigInteger left, BigInteger right)
        {
            return new BigInteger(left.bi * right.bi);
        }

        public static BigInteger operator /(BigInteger left, BigInteger right)
        {
            return new BigInteger(left.bi / right.bi);
        }

        public static BigInteger operator %(BigInteger dividend, BigInteger divisor)
        {
            return new BigInteger(dividend.bi % divisor.bi);
        }

        public static BigInteger operator &(BigInteger left, BigInteger right)
        {
            return new BigInteger(left.bi & right.bi);
        }

        public static BigInteger operator -(BigInteger value)
        {
            return new BigInteger(-value.bi);
        }

        public override string ToString()
        {
            return bi.ToString();
        }

        public string ToString(string format)
        {
            return bi.ToString(format);
        }

        public byte[] ToByteArray()
        {
            return bi.ToByteArray();
        }

        public static BigInteger Pow(BigInteger value, int exponent)
        {
            return new BigInteger(System.Numerics.BigInteger.Pow(value.bi, exponent));
        }

        //public static BigInteger Parse(string value)
        //{
        //    return new BigInteger(System.Numerics.BigInteger.Parse(value));
        //}

        public static BigInteger Parse(string value, NumberStyles style)
        {
            return new BigInteger(System.Numerics.BigInteger.Parse(value, style));
        }

        public static BigInteger Multiply(BigInteger left, BigInteger right)
        {
            return new BigInteger(System.Numerics.BigInteger.Multiply(left.bi, right.bi));
        }

        public int CompareTo(long other)
        {
            return bi.CompareTo(other);
        }

        public int CompareTo(object obj)
        {
            return bi.CompareTo(obj);
        }

        public int CompareTo(BigInteger value)
        {
            return bi.CompareTo(value.bi);
        }

        public static BigInteger Add(BigInteger left, BigInteger right)
        {
            return new BigInteger(System.Numerics.BigInteger.Add(left.bi, right.bi));
        }

    }
}
